﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ParserCore
{

    public sealed partial class Scanner : ScanBase
    {

        public bool LexerDebug { get; set; }

        public bool ScriptFile { get; set; }

        private bool TokenizerDone;
        private bool ProcessWordListCommand;

        private TokenStringPair[] TokenStringPairArray;
        private int Cursor;

        private BracketBraceParenNestingLevel NestingLevel;

        private int DoWork()
        {
            var tnk = this.TokenStringPairArray;

            if (ScriptFile)
            {
                ScriptFile = false;
                PrintDebugInfo("Token: SCRIPT");
                return (int)Tokens.SCRIPT;
            }

                if (!ProcessWordListCommand)
                {
                    switch (GetTokenRel(0))
                    {
                        case StupidTokens.S_END_OF_INPUT:
                            PrintDebugInfo("Token: END_OF_INPUT");
                            return (int)Tokens.END_OF_INPUT;
                        case StupidTokens.S_COMMENT:
                            Advance();
                            return DoWork();
                        case StupidTokens.S_CONTINUATION:
                            while (TokenStringPairArray[Cursor].Tok != StupidTokens.S_NEWLINE)
                                Advance();
                            Advance();
                            return DoWork();
                        case StupidTokens.S_NUMBER:
                            yylval.str_val = TokenStringPairArray[Cursor].Str;
                            Advance();
                            PrintDebugInfo("Token: NUM; " + TokenStringPairArray[Cursor - 1].Str );
                            return (int)Tokens.NUM;
                        case StupidTokens.S_IMAG_NUMBER:
                            yylval.str_val = TokenStringPairArray[Cursor].Str;
                            Advance();
                            PrintDebugInfo("Token: IMAG_NUM; " + TokenStringPairArray[Cursor - 1].Str);
                            return (int)Tokens.IMAG_NUM;

                        // Kljucne reci
                        case StupidTokens.S_KEYWORD_FUNCTION:
                            Advance();
                            PrintDebugInfo("Token: FCN");
                            return (int)Tokens.FCN;
                          
                        case StupidTokens.S_KEYWORD_IF:
                            Advance();
                            PrintDebugInfo("Token: IF");
                            return (int)Tokens.IF;
                            
                        case StupidTokens.S_KEYWORD_ELSE:
                            Advance();
                            PrintDebugInfo("Token: ELSE");
                            return (int)Tokens.ELSE;
                            
                        case StupidTokens.S_KEYWORD_ELSEIF:
                            Advance();
                            PrintDebugInfo("Token: ELSEIF");
                            return (int)Tokens.ELSEIF;
                            
                        case StupidTokens.S_KEYWORD_FOR:
                            Advance();
                            PrintDebugInfo("Token: FOR");
                            return (int)Tokens.FOR;
                            
                        case StupidTokens.S_KEYWORD_WHILE:
                            Advance();
                            PrintDebugInfo("Token: WHILE");
                            return (int)Tokens.WHILE;
                            
                        case StupidTokens.S_KEYWORD_SWITCH:
                            Advance();
                            PrintDebugInfo("Token: SWITCH");
                            return (int)Tokens.SWITCH;
                            
                        case StupidTokens.S_KEYWORD_CASE:
                            Advance();
                            PrintDebugInfo("Token: CASE");
                            return (int)Tokens.CASE;
                            
                        case StupidTokens.S_KEYWORD_OTHERWISE:
                            Advance();
                            PrintDebugInfo("Token: OTHERWISE");
                            return (int)Tokens.OTHERWISE;
                            
                        case StupidTokens.S_KEYWORD_BREAK:
                            Advance();
                            PrintDebugInfo("Token: BREAK");
                            return (int)Tokens.BREAK;
                            
                        case StupidTokens.S_KEYWORD_CONTINUE:
                            Advance();
                            PrintDebugInfo("Token: CONTINUE");
                            return (int)Tokens.CONTINUE;
                            
                        case StupidTokens.S_KEYWORD_TRY:
                            Advance();
                            PrintDebugInfo("Token: TRY");
                            return (int)Tokens.TRY;
                            
                        case StupidTokens.S_KEYWORD_CATCH:
                            Advance();
                            PrintDebugInfo("Token: CATCH");
                            return (int)Tokens.CATCH;
                            
                        case StupidTokens.S_KEYWORD_RETURN:
                            Advance();
                            PrintDebugInfo("Token: FUNC_RET");
                            return (int)Tokens.FUNC_RET;
                            
                        case StupidTokens.S_KEYWORD_END:         
                            var tok1 = GetTokenRel(-1);
                            var tok2 = GetTokenRel(-2);

                            if (tok1 == StupidTokens.S_COLON ||
                                (tok1 == StupidTokens.S_SPACE && tok2 == StupidTokens.S_COLON))
                            {
                                Advance();
                                return (int)Tokens.END;
                            }

                            if ((IsSep(-1))
                                || (IsSep(-2) && GetTokenRel(-1) == StupidTokens.S_SPACE))
                            {
                                Advance();
                                PrintDebugInfo("Token: END");
                                return (int)Tokens.END;
                            }
                            else
                            {
                                PrintDebugInfo("Token: LEXICAL_ERROR");
                                return (int)Tokens.LEXICAL_ERROR;
                            }
                            
                        case StupidTokens.S_KEYWORD_GLOBAL:
                            Advance();
                            PrintDebugInfo("Token: GLOBAL");
                            return (int)Tokens.GLOBAL;
                            
                        case StupidTokens.S_KEYWORD_PERSISTENT:
                            Advance();
                            PrintDebugInfo("Token: PERSISTENT");
                            return (int)Tokens.PERSISTENT;
                            

                        // Specijalni karakteri
                        case StupidTokens.S_DOT:
                            Advance();
                            PrintDebugInfo("Token: .");
                            return (int)'.';
                            
                        case StupidTokens.S_SEMICOLON:
                            Advance();
                            // zezalica da bi slučajevi poput [;3] mogli da rade

                            // napomena: [;1] radi, ali ne i [ ;1] -- srediti
                            if (NestingLevel.IsBrace() || NestingLevel.IsBracket())
                            {
                                if (GetTokenRel(-2) != StupidTokens.S_LBRACKET
                                 && GetTokenRel(-2) != StupidTokens.S_LBRACE)
                                {
                                    PrintDebugInfo("Token: ;");
                                    return (int)';';
                                }
                                else
                                    return DoWork();
                            }
                            else
                            {
                                PrintDebugInfo("Token: ;");
                                return (int)';';
                            }
                            
                        case StupidTokens.S_COMMA:
                            Advance();

                            if (NestingLevel.IsBrace() || NestingLevel.IsBracket())
                            {
                                // zezalica da bi slučajevi poput [,3], [3;,] mogli da rade
                                // napomena: [ 1 , ; , 1 ] => [ NUM , ; NUM ]
                                // prvi problematicni zarez je resen gramatikom
                                // drugi se resava ovde

                                // napomena: [,1] radi, ali ne i [ ,1] -- srediti
                                var tmpComma1 = GetTokenRel(-2) != StupidTokens.S_LBRACKET
                                             && GetTokenRel(-2) != StupidTokens.S_LBRACE
                                             && GetTokenRel(-2) != StupidTokens.S_SEMICOLON;
                                if (tmpComma1)
                                {
                                    PrintDebugInfo("Token: ,");
                                    return (int)',';
                                }
                                else
                                    return DoWork();
                            }
                            else
                            {
                                PrintDebugInfo("Token: ,");
                                return (int)',';
                            }
                            
                        case StupidTokens.S_COLON:
                            Advance();
                            PrintDebugInfo("Token: :");
                            return (int)':';
                            
                        case StupidTokens.S_AT:
                            Advance();
                            PrintDebugInfo("Token: @");
                            return (int)'@';
                            

                        // Operatori

                        case StupidTokens.S_OP_ASSIGN:
                            Advance();
                            PrintDebugInfo("Token: =");
                            return (int)'=';
                            
                        case StupidTokens.S_OP_NOT:
                            Advance();
                            PrintDebugInfo("Token: EXPR_NOT");
                            return (int)Tokens.EXPR_NOT;
                            
                        case StupidTokens.S_OP_POWER:
                            Advance();
                            PrintDebugInfo("Token: POW");
                            return (int)Tokens.POW;
                            
                        case StupidTokens.S_OP_LDIVIDE:
                            Advance();
                            PrintDebugInfo("Token: \\");
                            return (int)'\\';
                            
                        case StupidTokens.S_OP_RDIVIDE:
                            Advance();
                            PrintDebugInfo("Token: /");
                            return (int)'/';
                            
                        case StupidTokens.S_OP_MULTIPLY:
                            Advance();
                            PrintDebugInfo("Token: *");
                            return (int)'*';
                            
                        case StupidTokens.S_OP_PLUS:
                            Advance();
                            PrintDebugInfo("Token: +");
                            return (int)'+';
                            
                        case StupidTokens.S_OP_MINUS:
                            Advance();
                            PrintDebugInfo("Token: -");
                            return (int)'-';
                            
                        case StupidTokens.S_OP_OR:
                            Advance();
                            PrintDebugInfo("Token: EXPR_OR");
                            return (int)Tokens.EXPR_OR;
                            
                        case StupidTokens.S_OP_AND:
                            Advance();
                            PrintDebugInfo("Token: EXPR_AND");
                            return (int)Tokens.EXPR_AND;
                            
                        case StupidTokens.S_OP_OROR:
                            Advance();
                            PrintDebugInfo("Token: EXPR_OR_OR");
                            return (int)Tokens.EXPR_OR_OR;
                            
                        case StupidTokens.S_OP_ANDAND:
                            Advance();
                            PrintDebugInfo("Token: EXPR_AND_AND");
                            return (int)Tokens.EXPR_AND_AND;
                            
                        case StupidTokens.S_OP_GT:
                            Advance();
                            PrintDebugInfo("Token: EXPR_GT");
                            return (int)Tokens.EXPR_GT;
                            
                        case StupidTokens.S_OP_LT:
                            Advance();
                            PrintDebugInfo("Token: EXPR_LT");
                            return (int)Tokens.EXPR_LT;
                            
                        case StupidTokens.S_OP_GEQ:
                            Advance();
                            PrintDebugInfo("Token: EXPR_GE");
                            return (int)Tokens.EXPR_GE;
                            
                        case StupidTokens.S_OP_NEQ:
                            Advance();
                            PrintDebugInfo("Token: EXPR_NE");
                            return (int)Tokens.EXPR_NE;
                            
                        case StupidTokens.S_OP_EQ:
                            Advance();
                            PrintDebugInfo("Token: EXPR_EQ");
                            return (int)Tokens.EXPR_EQ;
                            
                        case StupidTokens.S_OP_LEQ:
                            Advance();
                            PrintDebugInfo("Token: EXPR_LE");
                            return (int)Tokens.EXPR_LE;
                            
                        case StupidTokens.S_OP_TRANSPOSE:
                            Advance();
                            PrintDebugInfo("Token: TRANSPOSE");
                            return (int)Tokens.TRANSPOSE;
                            
                        case StupidTokens.S_OP_EPOW:
                            Advance();
                            PrintDebugInfo("Token: EPOW");
                            return (int)Tokens.EPOW;
                            
                        case StupidTokens.S_OP_ELDIVIDE:
                            Advance();
                            PrintDebugInfo("Token: ELEFTDIV");
                            return (int)Tokens.ELEFTDIV;
                            
                        case StupidTokens.S_OP_ERDIVIDE:
                            Advance();
                            PrintDebugInfo("Token: EDIV");
                            return (int)Tokens.EDIV;
                            
                        case StupidTokens.S_OP_EMULTIPLY:
                            Advance();
                            PrintDebugInfo("Token: EMUL");
                            return (int)Tokens.EMUL;
                            
                        case StupidTokens.S_OP_EMINUS:
                            Advance();
                            PrintDebugInfo("Token: EMINUS");
                            return (int)Tokens.EMINUS;
                            
                        case StupidTokens.S_OP_EPLUS:
                            Advance();
                            PrintDebugInfo("Token: EPLUS");
                            return (int)Tokens.EPLUS;
                            
                        // zagrade
                        case StupidTokens.S_LPAREN:
                            NestingLevel.Paren();
                            Advance();
                            PrintDebugInfo("Token: (");
                            return (int)'(';
                            
                        case StupidTokens.S_RPAREN:
                            if (NestingLevel.IsParen())
                                NestingLevel.Remove();
                            else
                            {
                                // Parenthesis mismatch error
                                PrintDebugInfo("Token: LEXICAL_ERROR");
                                return (int)Tokens.LEXICAL_ERROR;
                            }
                            Advance();
                            PrintDebugInfo("Token: )");
                            return (int)')';
                            
                        case StupidTokens.S_LBRACKET:
                            NestingLevel.Bracket();
                            Advance();
                            PrintDebugInfo("Token: [");
                            return (int)'[';
                            
                        case StupidTokens.S_RBRACKET:
                            if (NestingLevel.IsBracket())
                                NestingLevel.Remove();
                            else
                            {
                                // Parenthesis mismatch error
                                PrintDebugInfo("Token: LEXICAL_ERROR");
                                return (int)Tokens.LEXICAL_ERROR;
                            }

                            // potrebno je utvrditi da li vracamo ] ili CLOSE_BRACE
                            // CLOSE_BRACE se vraca samo ako posle ] sledi = u dodelama
                            // ne i u deklaracijama funkcija
                            // zasto postoje 2 tokena za istu stvar?
                            // da bi smanjio broj konflikata u gramatici 
                            var tokAfterBracket = GetTokenRel(1);
                            if (tokAfterBracket == StupidTokens.S_SPACE)
                                tokAfterBracket = GetTokenRel(2);
                            if (tokAfterBracket == StupidTokens.S_OP_ASSIGN)
                            {
                                var functionExists = false;
                                for (int i = -1; ; i--)
                                {
                                    var tokBeforeBracket = GetTokenRel(i);
                                    if (tokBeforeBracket == StupidTokens.S_NOTHING
                                     || tokBeforeBracket == StupidTokens.S_NEWLINE
                                     || tokBeforeBracket == StupidTokens.S_SEMICOLON
                                     || tokBeforeBracket == StupidTokens.S_COMMA)
                                        break;
                                    if (tokBeforeBracket == StupidTokens.S_KEYWORD_FUNCTION)
                                    {
                                        functionExists = true;
                                    }
                                }
                                if (!functionExists)
                                {
                                    Advance();
                                    PrintDebugInfo("Token: CLOSE_BRACE");
                                    return (int)Tokens.CLOSE_BRACE;
                                }
                            }

                            Advance();
                            PrintDebugInfo("Token: ]");
                            return (int)']';
                            
                        case StupidTokens.S_LBRACE:
                            NestingLevel.Brace();
                            Advance();
                            PrintDebugInfo("Token: {");
                            return (int)'{';
                            
                        case StupidTokens.S_RBRACE:
                            if (NestingLevel.IsBrace())
                                NestingLevel.Remove();
                            else
                            {
                                // Parenthesis mismatch error
                                PrintDebugInfo("Token: LEXICAL_ERROR");
                                return (int)Tokens.LEXICAL_ERROR;
                            }
                            Advance();
                            PrintDebugInfo("Token: }");
                            return (int)'}';
                            

                        // "problematicni" tokeni

                        case StupidTokens.S_NAME:
                            // proveravamo da li imamo pocetak nove komande
                            var tokBeforeName = GetTokenRel(-1);
                            if (tokBeforeName == StupidTokens.S_SPACE)
                                tokBeforeName = GetTokenRel(-2);

                            // Word List Command processing
                            if (NestingLevel.None())
                            {
                                if (tokBeforeName == StupidTokens.S_NEWLINE
                                || tokBeforeName == StupidTokens.S_SEMICOLON
                                || tokBeforeName == StupidTokens.S_COMMA
                                || tokBeforeName == StupidTokens.S_NOTHING)
                                {
                                    if (GetTokenRel(1) == StupidTokens.S_SPACE
                                     && (GetTokenRel(2) != StupidTokens.S_LPAREN
                                       && GetTokenRel(2) != StupidTokens.S_LBRACKET
                                   && GetTokenRel(2) != StupidTokens.S_LBRACE
                                   && GetTokenRel(2) != StupidTokens.S_RPAREN
                                   && GetTokenRel(2) != StupidTokens.S_RBRACKET
                                   && GetTokenRel(2) != StupidTokens.S_RBRACE
                                   && GetTokenRel(2) != StupidTokens.S_OP_ASSIGN)
                                    && !(IsOp(2) && GetTokenRel(3) == StupidTokens.S_SPACE
                                       && GetTokenRel(2) != StupidTokens.S_OP_ASSIGN))
                                    {
                                        ProcessWordListCommand = true;
                                    }
                                }
                            }

                            yylval.str_val = TokenStringPairArray[Cursor].Str;
                            Advance();
                            PrintDebugInfo("Token: NAME; " + TokenStringPairArray[Cursor - 1].Str);
                            return (int)Tokens.NAME;
                            

                        case StupidTokens.S_SPACE:
                            // praznine se ponekad pretvaraju u zareze u izrazima koji grade matrice
                            // na primer:
                            //  [ 1 + 1 ] => [ NUM + NUM ]
                            //  [ 1 +1 ]  => [ NUM , + NUM ]
                            //  [ 1 1 ] => [ NUM , NUM ]
                            if (NestingLevel.IsBracket())
                            {
                                // trenutno smo u izrazu koji gradi matricu
                                // proveravamo da li prazninu treba konvertovati u zarez
                                var nextTok = GetTokenRel(1);
                                if ((nextTok == StupidTokens.S_OP_PLUS
                                     || nextTok == StupidTokens.S_OP_MINUS
                                     || nextTok == StupidTokens.S_OP_NOT)
                                && GetTokenRel(2) != StupidTokens.S_SPACE)
                                {
                                    Advance();
                                    PrintDebugInfo("Token: ,");
                                    return (int)',';
                                }
                                else if (IsOp(1) || IsOp(-1))
                                {
                                    Advance();
                                    return DoWork();
                                }
                                else if (GetTokenRel(1) == StupidTokens.S_SEMICOLON
                                     || GetTokenRel(1) == StupidTokens.S_COMMA
                                 || GetTokenRel(1) == StupidTokens.S_RBRACKET
                                     || GetTokenRel(-1) == StupidTokens.S_SEMICOLON
                                 || GetTokenRel(-1) == StupidTokens.S_COMMA
                                 || GetTokenRel(-1) == StupidTokens.S_LBRACKET)
                                {
                                    Advance();
                                    return DoWork();
                                }
                                else
                                {
                                    Advance();
                                    PrintDebugInfo("Token: ,");
                                    return (int)',';
                                }
                            }
                            else
                            {
                                Advance();
                                return DoWork();
                            }
                            

                        case StupidTokens.S_NEWLINE:
                            // znak za novi red se pretvara u ; ako smo u izrazu koji gradi
                            // matricu
                            Advance();
                            if (NestingLevel.IsBracket())
                            {
                                PrintDebugInfo("Token: ;");
                                return (int)';';
                            }
                            else
                            {
                                PrintDebugInfo("Token: NEWLINE");
                                return (int)'\n';
                            }
                            

                        case StupidTokens.S_QUOTE:
                            // treba odrediti da li treba vratiti operator transponovanja
                            // ili string
                            var prevTok = GetTokenRel(-1);
                            Advance();
                            if (prevTok == StupidTokens.S_RPAREN
                             || prevTok == StupidTokens.S_RBRACKET
                             || prevTok == StupidTokens.S_RBRACE
                             || prevTok == StupidTokens.S_NAME
                             || prevTok == StupidTokens.S_NUMBER
                             || prevTok == StupidTokens.S_IMAG_NUMBER)
                            {
                                PrintDebugInfo("Token: QUOTE");
                                return (int)Tokens.QUOTE;
                            }
                            else
                            {
                                // handle string
                                if (!HandleString())
                                {
                                    PrintDebugInfo("Token: LEXICAL_ERROR");
                                    return (int)Tokens.LEXICAL_ERROR;
                                }
                                PrintDebugInfo("Token: TSTRING; " + TokenStringPairArray[Cursor - 1].Str);
                                return (int)Tokens.TSTRING;
                            }
                            

                        default:
                            break;
                    }
                }
                else
                {
                    // procesiraj listu komandi

                    switch (GetTokenRel(0))
                    {
                        case StupidTokens.S_END_OF_INPUT:
                            PrintDebugInfo("Token: END_OF_INPUT");
                            return (int)Tokens.END_OF_INPUT;
                        case StupidTokens.S_SPACE:
                            Advance();
                            return DoWork();
                        case StupidTokens.S_NEWLINE:
                            ProcessWordListCommand = false;
                            Advance();
                            PrintDebugInfo("Token: NEWLINE");
                            return (int)'\n';
                        case StupidTokens.S_SEMICOLON:
                            ProcessWordListCommand = false;
                            Advance();
                            PrintDebugInfo("Token: ;");
                            return (int)';';
                        case StupidTokens.S_COMMA:
                            ProcessWordListCommand = false;
                            Advance();
                            PrintDebugInfo("Token: COMMA");
                            return (int)',';
                        case StupidTokens.S_COMMENT:
                            ProcessWordListCommand = false;
                            Advance();
                            return DoWork();
                        case StupidTokens.S_QUOTE:
                            Advance();
                            if (!HandleString())
                            {
                                PrintDebugInfo("Token: LEXICAL_ERROR");
                                return (int)Tokens.LEXICAL_ERROR;
                            }
                            PrintDebugInfo("Token: TSTRING; " + TokenStringPairArray[Cursor - 1].Str);
                            return (int)Tokens.TSTRING;

                        default:
                            // skupljaj sve osim dole navedenih tokena i vracaj string
                            var sbcom = new StringBuilder();
                            while (GetTokenRel(0) != StupidTokens.S_SPACE
                                && GetTokenRel(0) != StupidTokens.S_NEWLINE
                            && GetTokenRel(0) != StupidTokens.S_SEMICOLON
                            && GetTokenRel(0) != StupidTokens.S_COMMA
                            && GetTokenRel(0) != StupidTokens.S_COMMENT
                            && GetTokenRel(0) != StupidTokens.S_END_OF_INPUT
                            && GetTokenRel(0) != StupidTokens.S_NOTHING
                            && GetTokenRel(0) != StupidTokens.S_QUOTE)
                            {
                                sbcom.Append(TokenStringPairArray[Cursor].Str);
                                Advance();
                            }

                            yylval.str_val = sbcom.ToString();

                            PrintDebugInfo("Token: TSTRING; " + TokenStringPairArray[Cursor - 1].Str);
                            return (int)Tokens.TSTRING;
                    }
                }
            

            PrintDebugInfo("Token: LEXICAL_ERROR");
            return (int)Tokens.LEXICAL_ERROR;
        }

        public void Reset()
        {
            LexerDebug = false;

            TokenizerDone = false;
            ProcessWordListCommand = false;

            TokenStringPairArray = null;
            Cursor = 0;

            NestingLevel = new BracketBraceParenNestingLevel();
        }

        // Help gizmos        

        private enum StupidTokens
        {
            S_NEWLINE,
            S_SPACE,
            S_NAME,
            S_COMMENT,
            S_CONTINUATION,
            S_NUMBER,
            S_IMAG_NUMBER,

            S_KEYWORD_FUNCTION,
            S_KEYWORD_IF,
            S_KEYWORD_ELSE,
            S_KEYWORD_ELSEIF,
            S_KEYWORD_FOR,
            S_KEYWORD_WHILE,
            S_KEYWORD_SWITCH,
            S_KEYWORD_CASE,
            S_KEYWORD_OTHERWISE,
            S_KEYWORD_BREAK,
            S_KEYWORD_CONTINUE,
            S_KEYWORD_TRY,
            S_KEYWORD_CATCH,
            S_KEYWORD_RETURN,
            S_KEYWORD_END,
            S_KEYWORD_GLOBAL,
            S_KEYWORD_PERSISTENT,

            S_DOT,          //  .
            S_SEMICOLON,    //  ;
            S_COMMA,        //  ,
            S_COLON,        //  :
            S_QUOTE,        //  '
            S_AT,           //  @

            S_OP_ASSIGN,    //  =
            S_OP_NOT,       //  ~
            S_OP_POWER,     //  ^
            S_OP_LDIVIDE,   //  \
            S_OP_RDIVIDE,   //  /
            S_OP_MULTIPLY,  //  *
            S_OP_PLUS,      //  +
            S_OP_MINUS,     //  -
            S_OP_OR,        //  |
            S_OP_AND,       //  &
            S_OP_OROR,      //  ||
            S_OP_ANDAND,    //  &&
            S_OP_GT,        //  >
            S_OP_LT,        //  <
            S_OP_GEQ,       //  >=
            S_OP_NEQ,       //  ~=
            S_OP_EQ,        //  ==
            S_OP_LEQ,       //  <=
            S_OP_TRANSPOSE, //  .'
            S_OP_EPOW,      //  .^
            S_OP_ELDIVIDE,  //  .\
            S_OP_ERDIVIDE,  //  ./
            S_OP_EMULTIPLY, //  .*
            S_OP_EMINUS,    //  .-
            S_OP_EPLUS,     //  .+

            S_LPAREN,    //  (
            S_RPAREN,    //  )
            S_LBRACKET,  //  [
            S_RBRACKET,  //  ]
            S_LBRACE,    //  {
            S_RBRACE,    //  }  

            S_EXCLAMATION,  // za sistemske komande

            S_END_OF_INPUT,
            S_UNRECOGNISED,  // neprepoznat ulaz
            S_NOTHING        // tokenizator ne koristi ovaj token
        }

        private bool IsOp(int relpos)
        {
            var nextTok = GetTokenRel(relpos);
            switch (nextTok)
            {
                case StupidTokens.S_OP_NOT:
                case StupidTokens.S_OP_POWER:
                case StupidTokens.S_OP_LDIVIDE:
                case StupidTokens.S_OP_RDIVIDE:
                case StupidTokens.S_OP_MULTIPLY:
                case StupidTokens.S_OP_PLUS:
                case StupidTokens.S_OP_MINUS:
                case StupidTokens.S_OP_OR:
                case StupidTokens.S_OP_AND:
                case StupidTokens.S_OP_OROR:
                case StupidTokens.S_OP_ANDAND:
                case StupidTokens.S_OP_GT:
                case StupidTokens.S_OP_LT:
                case StupidTokens.S_OP_GEQ:
                case StupidTokens.S_OP_NEQ:
                case StupidTokens.S_OP_EQ:
                case StupidTokens.S_OP_LEQ:
                case StupidTokens.S_OP_TRANSPOSE:
                case StupidTokens.S_OP_EPOW:
                case StupidTokens.S_OP_ELDIVIDE:
                case StupidTokens.S_OP_ERDIVIDE:
                case StupidTokens.S_OP_EMULTIPLY:
                case StupidTokens.S_OP_EMINUS:
                case StupidTokens.S_OP_EPLUS:
                    return true;
                default:
                    return false;
            }
        }

        private bool IsSep(int relpos)
        {
            var tok = GetTokenRel(relpos);
            switch (tok)
            {
                case StupidTokens.S_NEWLINE:
                case StupidTokens.S_COMMA:
                case StupidTokens.S_SEMICOLON:
                    return true;
                default:
                    return false;
            }

        }

        // vraca token iz liste tokena relativno u odnosu na kursor
        // znaci  GetTokenRel(-1) vraca token pre onoga na koji pokazuje kursor
        private StupidTokens GetTokenRel(int relpos)
        {
            int realpos = Cursor + relpos;
            if ((realpos < 0) || (realpos > TokenStringPairArray.Length - 1))
                return StupidTokens.S_NOTHING;
            return TokenStringPairArray[realpos].Tok;
        }

        private void Advance()
        { ++Cursor; }


        private bool HandleString()
        {
            var sb = new StringBuilder();
            var wasQuote = false;
            var wasTransp = false;

            while (true)
            {
                var token = GetTokenRel(0);

                if (token == StupidTokens.S_NOTHING
                    || token == StupidTokens.S_END_OF_INPUT)
                    return false;
                else if (token == StupidTokens.S_OP_TRANSPOSE)
                {
                    if (GetTokenRel(1) == StupidTokens.S_QUOTE)
                    {
                        sb.Append(".");
                        wasTransp = true;
                        Advance();
                    }
                    else
                    {
                        sb.Append(".");
                        break;
                    }
                }
                else if (token == StupidTokens.S_QUOTE)
                {
                    if (wasQuote || wasTransp)
                    {
                        sb.Append("''");
                        wasQuote = false;
                        wasTransp = false;
                        Advance();
                    }
                    else if (GetTokenRel(1) == StupidTokens.S_QUOTE)
                    {
                        wasQuote = true;
                        Advance();
                    }
                    else
                    {
                        break;
                    }
                }
                else
                {
                    sb.Append(TokenStringPairArray[Cursor].Str);
                    Advance();
                }
            }


            yylval.str_val = sb.ToString();
            Advance();
            return true;
        }

        private class TokenStringPair
        {
            public StupidTokens Tok { get; private set; }
            public string Str { get; private set; }

            public TokenStringPair(StupidTokens tok, string str)
            {
                Tok = tok;
                Str = str;
            }
        }

        private void PrintDebugInfo(string msg)
        {
            if (LexerDebug)
                Console.WriteLine(msg);
        }

        private class BracketBraceParenNestingLevel
        {
            private enum BracketBraceParen { Bracket, Brace, Paren }
            private Stack<BracketBraceParen> Context;

            public BracketBraceParenNestingLevel()
            { Context = new Stack<BracketBraceParen>(); }

            public void Bracket()
            { Context.Push(BracketBraceParen.Bracket); }

            public void Brace()
            { Context.Push(BracketBraceParen.Brace); }

            public void Paren()
            { Context.Push(BracketBraceParen.Paren); }

            public bool IsBracket()
            {
                if (Context.Count != 0)
                    return Context.Peek() == BracketBraceParen.Bracket;
                else
                    return false;
            }

            public bool IsBrace()
            {
                if (Context.Count != 0)
                    return Context.Peek() == BracketBraceParen.Brace;
                else
                    return false;
            }

            public bool IsParen()
            {
                if (Context.Count != 0)
                    return Context.Peek() == BracketBraceParen.Paren;
                else
                    return false;
            }

            public bool None()
            {
                return Context.Count == 0;
            }

            public void Remove()
            {
                if (Context.Count != 0)
                    Context.Pop();
            }

            public void Clear()
            { Context.Clear(); }
        }


    }
}
